require "lunit"

OrderedSet = require "loop.collection.OrderedSet"
PriorityQueue = require "loop.collection.PriorityQueue"

local urlRequest = {}

local asyncQueue = {}

local timerId = 0
local timerTable = {}
local timerQueue = {}

local clientQueue = {}
local sceneQueue = {}
local worldQueue = {}

CHANNEL = {
	PRIVATE = 1,
	GROUP = 2,
	SCENE = 3,
}

local broadcastQueue = {}

local function getFunction(funcName)
	return nil
end
-- mock Log module
--[[
Log = {}

Log.warn = function(msg)
	print(msg)
end
--]]
-- mock Util module
Util = {}

Util.performUrl = function(url)
	return urlRequest[url]
end

Util.registerUrl = function(url, response)
	urlRequest[url] = response
end

-- mock Asynchronous module
Async = {}

Async.post = function(funcName, ...)
	OrderedSet.insert(asyncQueue, { funcName, { ... } })
end

Async.join = function(funcName, ...)
	local thread = coroutine.running()
	assert(thread ~= nil, "Call join from main thread")

	OrderedSet.insert(asyncQueue, { funcName, { ... }, thread })
	return coroutine.yield()
end

Async.runOne = function()
	if OrderedSet.empty(asyncQueue) then
		return
	end
	local handler = OrderedSet.popfront(asyncQueue)
	Log.debug(handler[1])

	if handler[3] then
		coroutine.resume(handler[3])
	end
end

Async.runAll = function()
	for handler in OrderedSet.sequence(asyncQueue) do
		Log.debug(handler[1])
		if handler[3] then
			coroutine.resume(handler[3])
		end
	end
	asyncQueue = {}
end

Async.reset = function()
	asyncQueue = {}
end

-- mock Timer module
Timer = {}

Timer.setTimeout = function(timeout, funcName, ...)
	timerId = timerId + 1
	timerTable[timerId] = { funcName, { ... }, 1 }

	PriorityQueue.enqueue(timerQueue, timerTable[timerId], timeout)
	return timerId
end

Timer.setInterval = function(timeout, interval, funcName, ...)
	timerId = timerId + 1
	timerTable[timerId] = { funcName, { ... }, 5 }

        PriorityQueue.enqueue(timerQueue, timerTable[timerId], timeout)
	return timerId
end

Timer.cancel = function(timerId)
	local handler = timerTable[timerId]
	if handler == nil then
		return
	end
	PriorityQueue.remove(timerQueue, handler)
	timerTable[timerId] = nil
end

Timer.run = function()
	for handler in PriorityQueue.sequence(timerQueue) do
		for idx = 1, handler[3] do
			Log.debug(handler[1] .. " " .. tostring(idx))
		end
	end
	timerQueue= {}
end

Timer.reset = function()
	timerQueue= {}
end

-- mock Client module
Client = {}

Client.send = function(player, request, ...)
	if not clientQueue[player] then
		clientQueue[player] = {}
	end
	OrderedSet.pushback(clientQueue[player], { request = request, arguments = { ... } })
end

Client.getTailRequest = function(player)
	if not clientQueue[player] or OrderedSet.empty(clientQueue[player]) then
		return nil
	end
	return OrderedSet.last(clientQueue[player])
end

-- mock Scene module
Scene = {}

Scene.forward = function(player, request, ...)
	OrderedSet.insert(sceneQueue, { player, requst, { ... } })
end

Scene.multicast = function(channel, group, request, ...)
	OrderedSet.insert(broadcastQueue, { channel, group, requst, { ... } })
end

Scene.broadcast = function(request, ...)
	OrderedSet.insert(broadcastQueue, { CHANNEL.SCENE, Scene.id, requst, { ... } })
end

-- mock World module
World = {}

World.call = function(request, ...)
	OrderedSet.insert(worldQueue, { requst, { ... } })
end

World.broadcast = function(request, ...)
	OrderedSet.insert(broadcastQueue, { CHANNEL.PRIVATE, 0, requst, { ... } })
end
